#pragma once

#include <boost/detail/workaround.hpp>

# pragma warning(push)
# pragma warning(disable:4996)
# pragma warning(disable:4510)
# pragma warning(disable:4610)

#include <cstddef>
#include <stdexcept>
#include <algorithm>
#include <string>
#include <sstream>
#include <boost/assert.hpp>
#include <boost/swap.hpp>
#include <boost/detail/iterator.hpp>
#include <boost/throw_exception.hpp>
#include <boost/config.hpp>

template<class T, std::size_t LEN = 0>
class Array {
private:
    T* elems;
    std::size_t N;
    std::size_t capality;
public:
    // type definitions
    typedef T              value_type;
    typedef T*             iterator;
    typedef const T*       const_iterator;
    typedef T&             reference;
    typedef const T&       const_reference;
    typedef std::size_t    size_type;
    typedef std::ptrdiff_t difference_type;

    Array():elems(0), N(0), capality(0) {
        resize(LEN, value_type());
    }

    Array(std::size_t len, const value_type& val = value_type()):elems(0), N(0), capality(0) {
        resize(len, val);
    }
	template<std::size_t LEN2>
	Array(const Array<T, LEN2>& other):elems(0), N(0), capality(0){
		resize(other.size());
        std::copy(other.begin(),other.end(), begin());
	}
    virtual ~Array() {
        if(elems) {
            delete[] elems;
        }
    }
    bool IncCapality(std::size_t newCapality, const value_type& val = value_type()) {
        if(newCapality <= capality) {
            return true;
        }
        T* p = new T[newCapality];
        if(!p) {
#ifdef _DEBUG
            throw exception("Alloc Memory Failed!");
#endif
            return false;
        }
        std::copy(begin(), end(), p);
        std::fill(p + N, p + newCapality, val);
        capality = newCapality;
        if(elems) {
            delete[] elems;
        }
        elems = p;
        return true;
    }
    bool resize(std::size_t newLen, const value_type& val = value_type()) {
        if(!IncCapality(newLen, val)) {
            return false;
        }
        N = newLen;
        return true;
    }
    // iterator support
    iterator begin() {
        if(!N) {
            return iterator( reinterpret_cast< T * >( this ) );
        }
        return elems;
    }
    const_iterator begin() const {
        if(!N) {
            return const_iterator(  reinterpret_cast< const T * >( this ) );
        }
        return elems;
    }
    iterator end() {
        if(!N) {
            return begin();
        }
        return elems+N;
    }
    const_iterator end() const {
        if(!N) {
            return begin();
        }
        return elems+N;
    }

    // reverse iterator support
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS)
    typedef std::reverse_iterator<iterator> reverse_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310)
    // workaround for broken reverse_iterator in VC7
    typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, iterator,
            reference, iterator, reference> > reverse_iterator;
    typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, const_iterator,
            const_reference, iterator, reference> > const_reverse_iterator;
#elif defined(_RWSTD_NO_CLASS_PARTIAL_SPEC)
    typedef std::reverse_iterator<iterator, std::random_access_iterator_tag,
            value_type, reference, iterator, difference_type> reverse_iterator;
    typedef std::reverse_iterator<const_iterator, std::random_access_iterator_tag,
            value_type, const_reference, const_iterator, difference_type> const_reverse_iterator;
#else
    // workaround for broken reverse_iterator implementations
    typedef std::reverse_iterator<iterator,T> reverse_iterator;
    typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
#endif

    reverse_iterator rbegin() {
        return reverse_iterator(end());
    }
    const_reverse_iterator rbegin() const {
        return const_reverse_iterator(end());
    }
    reverse_iterator rend() {
        return reverse_iterator(begin());
    }
    const_reverse_iterator rend() const {
        return const_reverse_iterator(begin());
    }

    // operator[]
    reference operator[](size_type i) {
        BOOST_ASSERT( i < N && "out of range" );
        return elems[i];
    }

    const_reference operator[](size_type i) const {
        BOOST_ASSERT( i < N && "out of range" );
        return elems[i];
    }

    // at() with range check
    reference at(size_type i) {
        rangecheck(i);
        return elems[i];
    }
    const_reference at(size_type i) const {
        rangecheck(i);
        return elems[i];
    }

    // front() and back()
    reference front() {
        rangecheck(0);
        return elems[0];
    }

    const_reference front() const {
        rangecheck(0);
        return elems[0];
    }

    reference back() {
        rangecheck(N-1);//if N==0, N-1 ==> FFFFFFFF, rangecheck failed!
        return elems[N-1];
    }

    const_reference back() const {
        rangecheck(N-1);
        return elems[N-1];
    }

    // size is constant
    size_type size() {
        return N;
    }
    size_type length() {
        return size();
    }
    bool empty() {
        return 0 == N;
    }
    size_type max_size() {
        return N;
    }

    // swap (note: linear complexity)
    template<std::size_t LEN2>
    void swap (Array<T, LEN2>& y) {
        assert(N == y.N);
        for (size_type i = 0; i < N; ++i)
            boost::swap(elems[i],y.elems[i]);
    }

    // direct access to data (read-only)
    const T* data() const {
        return elems;
    }
    T* data() {
        return elems;
    }

    // use Array as C Array (direct read/write access to data)
    T* c_array() {
        return elems;
    }

    // assignment with type conversion
    template <typename T2, std::size_t LEN2>
    Array<T, LEN>& operator= (Array<T2, LEN2>& rhs) {
        resize(rhs.size());
        std::copy(rhs.begin(),rhs.end(), begin());
        return *this;
    }
    template <typename T2, std::size_t LEN2>
    Array<T, LEN>& eat(Array<T2, LEN2>& rhs) {
        if(elems) {
            delete[] elems;
        }
        elems = rhs.elems;
        N = rhs.N;
        capality = rhs.capality;
        rhs.elems = 0;
        rhs.N = 0;
        rhs.capality = 0;
        return *this;
    }

    // assign one value to all elements
    void assign (const T& value) {
        fill ( value );    // A synonym for fill
    }
    void fill   (const T& value) {
        std::fill_n(begin(),size(),value);
    }

    // check range (may be private because it is )
    void rangecheck (size_type i) {
        if (i >= size()) {
            std::out_of_range e("Array<>: index out of range");
            boost::throw_exception(e);
        }
    }
};

// comparisons
template<typename T, std::size_t LEN1, std::size_t LEN2>
bool operator== (const Array<T, LEN1>& x, const Array<T, LEN2>& y) {
    return (x.N == y.N) && std::equal(x.begin(), x.end(), y.begin());
}
template<class T, std::size_t LEN1, std::size_t LEN2>
bool operator< (const Array<T, LEN1>& x, const Array<T, LEN2>& y) {
    return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end());
}
template<class T, std::size_t LEN1, std::size_t LEN2>
bool operator!= (const Array<T, LEN1>& x, const Array<T, LEN2>& y) {
    return !(x==y);
}
template<class T, std::size_t LEN1, std::size_t LEN2>
bool operator> (const Array<T, LEN1>& x, const Array<T, LEN2>& y) {
    return y<x;
}
template<class T, std::size_t LEN1, std::size_t LEN2>
bool operator<= (const Array<T, LEN1>& x, const Array<T, LEN2>& y) {
    return !(y<x);
}
template<class T, std::size_t LEN1, std::size_t LEN2>
bool operator>= (const Array<T, LEN1>& x, const Array<T, LEN2>& y) {
    return !(x<y);
}

// global swap()
template<class T, std::size_t LEN1, std::size_t LEN2>
inline void swap (Array<T, LEN1>& x, Array<T, LEN2>& y) {
    x.swap(y);
}


# pragma warning(pop)

//for log
#ifdef __SIMPLE_LOG__
template<typename T, std::size_t LEN>
Logger& operator <<(Logger& slog, Array<T, LEN>& val) {
    slog<<"Array:[";
    Array<T, LEN>::const_iterator it;
    for(it = val.begin(); it != val.end(); ++it) {
        if(it != val.begin()) {
            slog<<", ";
        }
        slog<<(*it);
    }
    slog<<"] ";
    return slog;
}
template<typename T, std::size_t LEN>
Logger& operator << (Logger& slog, const Array<T, LEN>& val) {
    return operator <<(slog, const_cast<Array<T, LEN>&>(val));
}
#endif


